/*
 * 创建日期 2009-12-7
 *
 * 成都天和软件公司
 * 电话：028-85425861
 * 传真：028-85425861-8008
 * 邮编：610041 
 * 版权所有
 */
package  org.fantasy.common.grid.report;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import  org.fantasy.common.db.Query;
import  org.fantasy.common.db.bean.ReportCell;
import  org.fantasy.common.db.bean.ReportRow;
import  org.fantasy.common.db.config.Options;
import  org.fantasy.common.db.oracle.OracleQryFactory;
import  org.fantasy.common.util.Debug;

/**
 *  报表数据分析器
 * @author: 王文成
 * @version: 1.0
 * @since 2009-12-7
 */
public class ReportDataParser {

    private static Log log = LogFactory.getLog(ReportDataParser.class);

    /**
     * 查询结果
     */
    private List<ReportRow> dataList;

    /**
     * 需要合并的列
     */
    private List<LinkCol> statCols;

    /**
     * 需要统计的列
     */
    private List<String> sumCols;

    /**
     * 是否小计
     */
    private boolean isSumTotal;

    /**
     * 是否合计
     */
    private boolean isSubTotal;

    /**
     * 分析结果
     */
    private List<ReportRow> resultList;

    public void setDataList(List<ReportRow> dataList) {
        this.dataList = dataList;
    }

    public void setSubTotal(boolean isSubTotal) {
        this.isSubTotal = isSubTotal;
    }

    public void setSumTotal(boolean isSumTotal) {
        this.isSumTotal = isSumTotal;
    }

    public void setStatCols(List<String> statCols) {
        this.statCols = covertLinkList(statCols);
    }

    public void setSumCols(List<String> sumCols) {
        this.sumCols = sumCols;
    }

    /**
     * 数据解析
     */
    public void parse() throws Exception {
        
        if( !dataList.isEmpty() ){
            // 数据分析
            this.analyse();
            // 默认分析结果
            this.resultList = dataList;
            // 生成小计
            if (isSubTotal)
                this.resultList = getSubTotalRows();
            // 增加合计
            if (isSumTotal)
                this.resultList.add(getSumTotalRow());
        }else{
            this.resultList = new ArrayList<ReportRow>(0);
        }
    }

    /**
     * 分析数据
     */
    private void analyse() {
        for (LinkCol col : statCols) {
            ReportCell topCell = null;
            for (int i = 0; i < dataList.size(); i++) {
                ReportRow row = dataList.get(i);
                ReportCell cell = row.getReportCell(col.toString());
                boolean isCurTop = isCurTop(cell, i);
                boolean isPreTop = isPreTop(row, col);
                boolean isTop = isCurTop || isPreTop;
                cell.setTop(isTop);
                if (isTop) {
                    topCell = cell;
                } else {
                    topCell.increaseRowspan();
                    cell.setTopCell(topCell);
                }
            }
        }
    }

    /**
     * 计算报表小计数据
     * 
     * @throws Exception
     */
    private List<ReportRow> getSubTotalRows() throws Exception {
        List<ReportRow> rows = new ArrayList<ReportRow>(dataList);
        for (LinkCol col : statCols) {
            rows = accountRows(rows, col);
        }
        return rows;
    }

    /**
     * 计算所有行
     * 
     * @param rows
     * @param colName
     * @param preColName
     * @return
     */
    private List<ReportRow> accountRows(List<ReportRow> rows, LinkCol col) {
        int size = (int) (rows.size() * 1.1);
        List<ReportRow> list = new ArrayList<ReportRow>(size);
        int start = 0, end = 0;
        for (int i = 0; i < rows.size(); i++) {
            ReportRow row = rows.get(i);
            boolean isAdd = isAddSubSumCol(rows, col, i);
            if (isAdd) {
                ReportRow subSumRow = getSubTotalRow(rows, start, end, col);
                // 之前的列rowspan+1
                increaseRowspan(rows.get(i - 1), col);
                start = end;
                list.add(subSumRow);
            }
            end++;
            list.add(row);
        }

        // 最后一行不是小计行
        ReportRow lastRow = rows.get(rows.size() - 1);
        if (!lastRow.isSubTotal()) {
            ReportRow subSumRow = getSubTotalRow(rows, start, end, col);
            list.add(subSumRow);
        }
        return list;
    }

    /**
     * 是否是Rowpspan Col
     * 
     * @param row
     * @param col
     * @return
     */
    private boolean isAddSubSumCol(List<ReportRow> rows, LinkCol col, int i) {
        if (i == 0)
            return false;
        ReportRow preRow = rows.get(i - 1);
        ReportRow curRow = rows.get(i);
        ReportCell curCell = curRow.getReportCell(col.getColName());
        return !preRow.isSubTotal() && curCell.isTop();
    }

    /**
     * 当前列之前的列增加rowspan
     * 
     * @param row
     * @param col
     */
    private void increaseRowspan(ReportRow preRow, LinkCol col) {
        while ((col = col.getPrevious()) != null) {
            ReportCell cell = preRow.getReportCell(col.getColName());
            cell.getTopCell().increaseRowspan();
        }
    }

    /**
     * 当前一列是否是第一级
     * 
     * @param list
     * @param colName
     * @param i
     * @return
     */
    private boolean isCurTop(ReportCell cell, int i) {
        if (i == 0)
            return true;
        String curValue = cell.getString();
        ReportCell yPreCell = cell.yPrevious();
        String preValue = yPreCell.getString();
        return !curValue.equals(preValue);
    }

    /**
     * 上一列是否是第一级
     * 
     * @param list
     * @param colName
     * @param i
     * @return
     */
    private boolean isPreTop(ReportRow row, LinkCol col) {
        LinkCol preCol = col.getPrevious();
        if (preCol == null)
            return false;
        ReportCell preCell = row.getReportCell(preCol.toString());
        return preCell.isTop();
    }

    /**
     * 转化为链表
     * 
     * @return
     */
    private List<LinkCol> covertLinkList(List<String> cols) {
        List<LinkCol> statCols = new ArrayList<LinkCol>();
        LinkCol preEntry = null;
        for (String value : cols) {
            LinkCol entry = new LinkCol();
            entry.setColName(value);
            entry.setPrevious(preEntry);
            if (preEntry != null)
                preEntry.setNext(entry);
            statCols.add(entry);
            preEntry = entry;
        }
        return statCols;
    }

    public List<ReportRow> getResultList() {
        return resultList;
    }

    /**
     * 计算小计行
     * 
     * @param rows
     * @param start
     * @param end
     * @param col
     * @return
     */
    public ReportRow getSubTotalRow(List<ReportRow> rows, int start, int end, LinkCol col) {
        ReportRow subTotalRow = newSubTotalRow(col);
        for (int i = start; i < end; i++) {
            ReportRow row = rows.get(i);
            if (!row.isSubTotal()) {
                for (String sumCol : sumCols) {
                    ReportCell cell = row.getReportCell(sumCol);
                    ReportCell subSumCell = subTotalRow.getReportCell(sumCol);
                    subSumCell.add(cell);
                }
            }
        }
        return subTotalRow;
    }

    /**
     * 计算合计行
     * 
     * @param rows
     * @param start
     * @param end
     * @param col
     * @return
     */
    public ReportRow getSumTotalRow() {
        ReportRow sumTotalRow = newSumTotalRow(statCols.get(0));
        for (ReportRow row : dataList) {
            for (String sumCol : sumCols) {
                ReportCell cell = row.getReportCell(sumCol);
                ReportCell sumCell = sumTotalRow.getReportCell(sumCol);
                sumCell.add(cell);
            }
        }
        return sumTotalRow;
    }

    /**
     * 取得新增行
     * 
     * @param cols
     * @param sumCol
     * @return
     */
    private ReportRow newSubTotalRow(LinkCol curCol) {
        ReportRow row = new ReportRow();
        row.setSubTotal(true);
        row.setStatCol(curCol.getColName());
        boolean top = false;
        for (LinkCol col : statCols) {
            ReportCell cell = new ReportCell();
            if (col.equals(curCol)) {
                cell.setValue("小计");
                top = true;
            }
            cell.setColName(col.getColName());
            cell.setTop(top);
            cell.setColClass(String.class);
            row.put(col.getColName(), cell);
        }
        return row;
    }

    /**
     * 取得新增行
     * 
     * @param cols
     * @param sumCol
     * @return
     */
    private ReportRow newSumTotalRow(LinkCol curCol) {
        ReportRow row = new ReportRow();
        row.setSubTotal(true);
        row.setStatCol(curCol.getColName());
        for (LinkCol col : statCols) {
            ReportCell cell = new ReportCell();
            cell.setColName(col.getColName());
            cell.setTop(true);
            cell.setColClass(String.class);
            if (col.equals(curCol))
                cell.setValue("合计");
            row.put(col.getColName(), cell);
        }
        return row;
    }

    public static void main(String[] args) throws Exception {

        OracleQryFactory qryFactory = new OracleQryFactory();
        qryFactory.setOptions(new Options());
        Query query = qryFactory
                .getQuery("select region_name , staff_grade_name , staff_name , charge1 , charge2 from report_test_2");
        List<ReportRow> list = query.report();
        List<String> statCols = Arrays.asList(new String[] { "regionName", "staffGradeName" });
        List<String> sumCols = Arrays.asList(new String[] { "charge1", "charge2" });
        Debug d = new Debug("Report Test");
        d.start("TEST");
        ReportDataParser report = new ReportDataParser();
        report.setDataList(list);
        report.setStatCols(statCols);
        report.setSumCols(sumCols);
        report.setSumTotal(true);
        report.setSubTotal(true);
        report.parse();
        List<ReportRow> rows = report.getResultList();
        StringBuffer s = new StringBuffer();
        for (int i = 0; i < rows.size(); i++) {
            ReportRow row = rows.get(i);
            s.append("isSum=" + row.isSumTotal() + ";isSubSum=" + row.isSubTotal());
            s.append(";regionName=" + row.getReportCell("regionName").getDesc() + ";staffGradeName="
                    + row.getReportCell("staffGradeName").getDesc());
            s.append(";charge1=" + row.getCell("charge1").getValue() + ";charge2=" + row.getCell("charge2").getValue());
            s.append("\n");
            s.append(row + "\n");
        }
        d.end("TEST");
        log.debug(s);
    }
}
